package com.example.demo.searcher;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ling
 * Date: 2023-02-04
 * Time: 12:08
 */

import org.springframework.beans.factory.annotation.Autowired;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 通过这个类来完成制作索引的过程
 * 读取之前下载好的这些文档，然后去解析文档的内容，并完成索引的制作
 */
public class Parser {
    //先指定一个加载文档的路径
    private static final String INPUT_PATH="D:/doc_searcher_index/jdk-8u361-docs-all/docs/api";


  //创建Index实例
    private Index index = new Index();

    public static void main(String[] args) throws InterruptedException {
        //制作索引的入口
        Parser parser = new Parser();
        parser.runByThread();
    }

    /**
     * 通过这个方法多线程制作索引
     */
    private void runByThread() throws InterruptedException {
        long beg = System.currentTimeMillis();
        System.out.println("开始制作索引");
        //1.根据上面指定的路径，枚举出该路径中所有的文件（只限html）放到file里面（递归）
        ArrayList<File> files = new ArrayList<>();
        enumFile(INPUT_PATH,files);
        //2.//2.循环遍历文件，多线程制作索引
        CountDownLatch latch = new CountDownLatch(files.size());
        ExecutorService executorService = Executors.newFixedThreadPool(4);
        for (File f : files) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("开始解析："+f.getAbsolutePath());
                    //解析html
                    parseHTML(f);
                    latch.countDown();
                }
            });
        }

        //3.把内存中构造好的索引数据结构保存到指定的文件中
        latch.await();
        executorService.shutdown();//手动关闭线程池
        index.save();
        long end = System.currentTimeMillis();
        System.out.println("索引制作消耗时间："+(end-beg));
    }

    /**
     * 通过这个方法，递归扫描文档
     * @param inputPath
     * @param fileList
     */
    private void enumFile(String inputPath,ArrayList<File> fileList) {
        File rootPath = new File(inputPath);
        //listFiles方法能获取到当前目录的文件和方法
        File[] files = rootPath.listFiles();
        for(File file : files) {
            if(file.isDirectory()) {
                enumFile(file.getAbsolutePath(),fileList);
            } else {
                if(file.getAbsolutePath().endsWith(".html")) {
                    fileList.add(file);
                }
            }
        }
    }

    /**
     * 通过这个方法解析Html文档，然后将文档新增到索引中去
     * 一条搜索结果要展示标题，描述,URL，这些信息就来自于要解析html,要得到描述要先得到正文
     * @param f
     */
    private void parseHTML(File f) {
        //1.解析出html的标题
        String title = parseTitle(f);
        //2.解析出html的url
        String url = parseURL(f);
        //3.解析出html的正文
        String content = parseContentByRegex(f);
        //4.将解析出来的信息加入到索引中去
        index.addDoc(title,url,content);

    }

    private String parseTitle(File f) {
        //由于文件名就是标题，所以直接提取文件名即可
        String name = f.getName();
        //取文件名作为标题，但是要把.html后缀给删除掉
        return name.substring(0,name.length()-".html".length());
    }

    /**
     * 最终跳转的URL以 https://docs.oracle.com/javase8/docs/api 为固定前缀
     * 然后提取本地文档路径的后半部分去和前缀拼接
     * @param f
     * @return
     */
    private String parseURL(File f) {
        String part1 = "https://docs.oracle.com/javase/8/docs/api";
        String part2 = f.getAbsolutePath().substring(INPUT_PATH.length());
        return part1+part2;
    }

    /**
     * 这个方法内部就是基于正则表达式实现去标签
     * @param f
     * @return
     */
    private String parseContentByRegex(File f) {
        //1.先把整个文件读到一个String中
        String content = readFile(f);
        //2.替换掉script标签
        content = content.replaceAll("<script.*?>(.*?)</script>"," ");
        //3.替换掉普通的html标签
        content = content.replaceAll("<.*?>"," ");
        //4.将多余空格删除
        content = content.replaceAll("\\s+"," ");
        return content;
    }

    private String readFile(File f) {
        try(BufferedReader bufferedReader = new BufferedReader(new FileReader(f))) {
            StringBuilder content = new StringBuilder();
            while(true) {
                int ret = bufferedReader.read();
                if(ret==-1) {
                    //说明读取完毕
                    break;
                }
                char c= (char) ret;
                if(c=='\n' || c=='\r') {
                    c=' ';
                }
                content.append(c);
            }
            return content.toString();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }

}
